package alifunserve

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
)

//AliFunHandler ...
type aliFunHandler struct {
	handler     http.Handler
	stripPrefix string
	initialize  func() error
}

//aliRequestData ...
type aliRequestData struct {
	Body            string            `json:"body"`
	Headers         map[string]string `json:"headers"`
	HTTPMethod      string            `json:"httpMethod"`
	IsBase64Encoded bool              `json:"isBase64Encoded"`
	Path            string            `json:"path"`
	PathParameters  map[string]string `json:"pathParameters"`
	QueryParameters map[string]string `json:"queryParameters"`
}

//aliResponseData ...
type aliResponseData struct {
	header http.Header
	writer *bytes.Buffer
	code   int
}

//Header ...
func (w *aliResponseData) Header() http.Header {
	return w.header
}

func (w *aliResponseData) Write(b []byte) (int, error) {
	return w.writer.Write(b)
}

//WriteHeader ...
func (w *aliResponseData) WriteHeader(statusCode int) {
	w.code = statusCode
}

//new Handler ...
func New(handler http.Handler, initialize func() error, stripPrefix string) *aliFunHandler {
	return &aliFunHandler{
		handler:     handler,
		initialize:  initialize,
		stripPrefix: stripPrefix,
	}
}
func (a *aliFunHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	path := r.Header.Get("x-fc-control-path")
	if path == "/initialize" {
		if a.initialize != nil {
			if err := a.initialize(); err != nil {
				log.Println("初始化失败", err.Error())
				w.WriteHeader(404)
				return
			}
		}
		w.WriteHeader(200)
	} else if path == "/invoke" {
		bt, _ := ioutil.ReadAll(r.Body)
		aliReq := &aliRequestData{}
		_ = json.Unmarshal(bt, aliReq)
		if aliReq.HTTPMethod != "" {
			uri, _ := url.ParseRequestURI(aliReq.Path)
			query := uri.Query()
			for k, v := range aliReq.QueryParameters {
				query.Set(k, v)
			}
			uri.RawQuery = query.Encode()
			myReq, _ := http.NewRequest(aliReq.HTTPMethod, uri.String(), nil)
			for k, v := range aliReq.Headers {
				myReq.Header.Set(k, v)
			}
			myRes := &aliResponseData{
				writer: bytes.NewBufferString(""),
				header: http.Header{},
			}
			a.handler.ServeHTTP(myRes, myReq)
			if myRes.code == 0 {
				myRes.code = 200
			}
			headers := map[string]string{}
			for k := range myRes.header {
				headers[k] = myRes.header.Get(k)
			}
			content := map[string]interface{}{}
			content["body"] = myRes.writer.Bytes()
			content["statusCode"] = myRes.code
			content["headers"] = headers
			content["isBase64Encoded"] = true
			data, _ := json.Marshal(content)
			w.Header().Set("Content-Type", "application/json")
			_, _ = w.Write(data)
		}
	} else if a.handler != nil {
		handler := a.handler
		if path == "/http-invoke" && a.stripPrefix != "" {
			handler = http.StripPrefix(a.stripPrefix, handler)
		}
		handler.ServeHTTP(w, r)
	}
}
